<template>
    <div ref="wrap">
        <div
            :style="leftSwitch"
            v-if="navigation"
            :class="leftSwitchClass"
            @click="leftSwitchClick"
        >
            <slot name="left-switch"></slot>
        </div>
        <div
            :style="rightSwitch"
            v-if="navigation"
            :class="rightSwitchClass"
            @click="rightSwitchClick"
        >
            <slot name="right-switch"></slot>
        </div>
        <div
            ref="realBox"
            :style="pos"
            @mouseenter="enter"
            @mouseleave="leave"
            @touchstart="touchStart"
            @touchmove="touchMove"
            @touchend="touchEnd"
        >
            <div ref="slotList" :style="float">
                <slot></slot>
            </div>
            <div v-html="copyHtml" :style="float"></div>
        </div>
    </div>
</template>

<script>
require("comutils/animationFrame")();
const arrayEqual = require("comutils/arrayEqual");
const copyObj = require("comutils/copyObj");
export default {
    name: "vue-seamless-scroll",
    data() {
        return {
            xPos: 0,
            yPos: 0,
            delay: 0,
            copyHtml: "",
            height: 0,
            width: 0, // 外容器宽度
            realBoxWidth: 0 // 内容实际宽度
        };
    },
    props: {
        data: {
            type: Array,
            default: () => {
                return [];
            }
        },
        classOption: {
            type: Object,
            default: () => {
                return {};
            }
        }
    },
    computed: {
        leftSwitchState() {
            return this.xPos < 0;
        },
        rightSwitchState() {
            return Math.abs(this.xPos) < this.realBoxWidth - this.width;
        },
        leftSwitchClass() {
            return this.leftSwitchState ? "" : this.options.switchDisabledClass;
        },
        rightSwitchClass() {
            return this.rightSwitchState
                ? ""
                : this.options.switchDisabledClass;
        },
        leftSwitch() {
            return {
                position: "absolute",
                margin: `${this.height / 2}px 0 0 -${
                    this.options.switchOffset
                }px`,
                transform: "translate(-100%,-50%)"
            };
        },
        rightSwitch() {
            return {
                position: "absolute",
                margin: `${this.height / 2}px 0 0 ${
                    this.width + this.options.switchOffset
                }px`,
                transform: "translateY(-50%)"
            };
        },
        float() {
            return this.isHorizontal
                ? { float: "left", overflow: "hidden" }
                : { overflow: "hidden" };
        },
        pos() {
            return {
                transform: `translate(${this.xPos}px,${this.yPos}px)`,
                transition: `all ${this.ease} ${this.delay}ms`,
                overflow: "hidden"
            };
        },
        defaultOption() {
            return {
                step: 1, //步长
                limitMoveNum: 5, //启动无缝滚动最小数据数
                hoverStop: true, //是否启用鼠标hover控制
                direction: 2, // 0 往下 1 往上 2向左 3向右
                openTouch: true, //开启移动端touch
                singleHeight: 0, //单条数据高度有值hoverStop关闭
                // singleWidth: this.realSingleStopWidth * 5, //单条数据宽度有值hoverStop关闭
                singleWidth: 1900, //单条数据宽度有值hoverStop关闭
                waitTime: 2000, //单步停止等待时间
                switchOffset: 30,
                autoPlay: true,
                navigation: false,
                switchSingleStep: 134,
                switchDelay: 400,
                switchDisabledClass: "disabled",
                isSingleRemUnit: false // singleWidth/singleHeight 是否开启rem度量
            };
        },
        options() {
            let ctx = copyObj({}, this.defaultOption, this.classOption);
            return ctx;
        },
        navigation() {
            return this.options.navigation;
        },
        autoPlay() {
            if (this.navigation) return false;
            return this.options.autoPlay;
        },
        scrollSwitch() {
            return this.data.length >= this.options.limitMoveNum;
        },
        hoverStopSwitch() {
            return this.options.hoverStop && this.autoPlay && this.scrollSwitch;
        },
        canTouchScroll() {
            return this.options.openTouch;
        },
        isHorizontal() {
            return this.options.direction > 1;
        },
        baseFontSize() {
            return this.options.isSingleRemUnit
                ? parseInt(
                      window.getComputedStyle(document.documentElement, null)
                          .fontSize
                  )
                : 1;
        },
        realSingleStopWidth() {
            return this.options.singleWidth * this.baseFontSize;
        },
        realSingleStopHeight() {
            return this.options.singleHeight * this.baseFontSize;
        },
        step() {
            let singleStep;
            let step = this.options.step;
            if (this.isHorizontal) {
                singleStep = this.realSingleStopWidth;
            } else {
                singleStep = this.realSingleStopHeight;
            }
            if (singleStep > 0 && singleStep % step > 0) {
                console.error(
                    "如果设置了单步滚动,step需是单步大小的约数,否则无法保证单步滚动结束的位置是否准确。~~~~~"
                );
            }
            return step;
        }
    },
    methods: {
        reset() {
            this._cancle();
            this._initMove();
        },
        leftSwitchClick() {
            if (!this.leftSwitchState) return;
            // 小于单步距离
            if (Math.abs(this.xPos) < this.options.switchSingleStep) {
                this.xPos = 0;
                return;
            }
            this.xPos += this.options.switchSingleStep;
        },
        rightSwitchClick() {
            if (!this.rightSwitchState) return;
            // 小于单步距离
            if (
                this.realBoxWidth - this.width + this.xPos <
                this.options.switchSingleStep
            ) {
                this.xPos = this.width - this.realBoxWidth;
                return;
            }
            this.xPos -= this.options.switchSingleStep;
        },
        _cancle() {
            cancelAnimationFrame(this.reqFrame || "");
        },
        touchStart(e) {
            if (!this.canTouchScroll) return;
            let timer;
            const touch = e.targetTouches[0]; //touches数组对象获得屏幕上所有的touch，取第一个touch
            const { waitTime, singleHeight, singleWidth } = this.options;
            this.startPos = {
                x: touch.pageX,
                y: touch.pageY
            }; //取第一个touch的坐标值
            this.startPosY = this.yPos; //记录touchStart时候的posY
            this.startPosX = this.xPos; //记录touchStart时候的posX
            if (!!singleHeight && !!singleWidth) {
                if (timer) clearTimeout(timer);
                timer = setTimeout(() => {
                    this._cancle();
                }, waitTime + 20);
            } else {
                this._cancle();
            }
        },
        touchMove(e) {
            //当屏幕有多个touch或者页面被缩放过，就不执行move操作
            if (
                !this.canTouchScroll ||
                e.targetTouches.length > 1 ||
                (e.scale && e.scale !== 1)
            )
                return;
            const touch = e.targetTouches[0];
            const { direction } = this.options;
            this.endPos = {
                x: touch.pageX - this.startPos.x,
                y: touch.pageY - this.startPos.y
            };
            event.preventDefault(); //阻止触摸事件的默认行为，即阻止滚屏
            const dir =
                Math.abs(this.endPos.x) < Math.abs(this.endPos.y) ? 1 : 0; //dir，1表示纵向滑动，0为横向滑动
            if (dir === 1 && direction < 2) {
                // 表示纵向滑动 && 运动方向为上下
                this.yPos = this.startPosY + this.endPos.y;
            } else if (dir === 0 && direction > 1) {
                // 为横向滑动 && 运动方向为左右
                this.xPos = this.startPosX + this.endPos.x;
            }
        },
        touchEnd() {
            if (!this.canTouchScroll) return;
            let timer;
            const direction = this.options.direction;
            this.delay = 50;
            if (direction === 1) {
                if (this.yPos > 0) this.yPos = 0;
            } else if (direction === 0) {
                let h = (this.realBoxHeight / 2) * -1;
                if (this.yPos < h) this.yPos = h;
            } else if (direction === 2) {
                if (this.xPos > 0) this.xPos = 0;
            } else if (direction === 3) {
                let w = this.realBoxWidth * -1;
                if (this.xPos < w) this.xPos = w;
            }
            if (timer) clearTimeout(timer);
            timer = setTimeout(() => {
                this.delay = 0;
                this._move();
            }, this.delay);
        },
        enter() {
            if (this.hoverStopSwitch) this._stopMove();
        },
        leave() {
            if (this.hoverStopSwitch) this._startMove();
        },
        _move() {
            const that = this;
            // 鼠标移入时拦截_move()
            if (this.isHover) return;
            that._cancle(); //进入move立即先清除动画 防止频繁touchMove导致多动画同时进行
            that.reqFrame = requestAnimationFrame(
                function () {
                    const h = that.realBoxHeight / 2; //实际高度
                    const w = that.realBoxWidth / 2; //宽度
                    let { direction, waitTime } = that.options;
                    let { step } = that;

                    if (direction === 1) {
                        // 上
                        if (Math.abs(that.yPos) >= h) {
                            that.$emit("ScrollEnd");
                            that.yPos = 0;
                        }
                        that.yPos -= step;
                    } else if (direction === 0) {
                        // 下
                        if (that.yPos >= 0) {
                            that.$emit("ScrollEnd");
                            that.yPos = h * -1;
                        }
                        that.yPos += step;
                    } else if (direction === 2) {
                        // 左
                        if (Math.abs(that.xPos) >= w) {
                            that.$emit("ScrollEnd");
                            that.xPos = 0;
                        }
                        that.xPos -= step;
                    } else if (direction === 3) {
                        // 右
                        if (that.xPos >= 0) {
                            that.$emit("ScrollEnd");
                            that.xPos = w * -1;
                        }
                        that.xPos += step;
                    }
                    if (that.singleWaitTime) clearTimeout(that.singleWaitTime);
                    //是否启动了单行暂停配置
                    if (that.realSingleStopHeight) {
                        // 一条数据时不改变移动位置
                        if (
                            Math.abs(that.yPos) % that.realSingleStopHeight <
                            step
                        ) {
                            // 符合条件暂停waitTime
                            that.singleWaitTime = setTimeout(() => {
                                if (that.data.length == 1) {
                                    return false;
                                }
                                that._move();
                                let topIndex =
                                    Math.abs(that.yPos) /
                                        that.realSingleStopHeight +
                                    1;
                                if (topIndex > that.data.length) {
                                    topIndex = 1;
                                } else if (topIndex == that.data.length) {
                                    topIndex = 0;
                                }
                                that.$emit("singleStepEnd", {
                                    topIndex: topIndex,
                                    ...that.data[topIndex]
                                });
                            }, waitTime);
                        } else {
                            that._move();
                        }
                    } else if (that.realSingleStopWidth) {
                        if (
                            Math.abs(that.xPos) % that.realSingleStopWidth <
                            step
                        ) {
                            // 符合条件暂停waitTime
                            that.singleWaitTime = setTimeout(() => {
                                that._move();
                            }, waitTime);
                        } else {
                            that._move();
                        }
                    } else {
                        that._move();
                    }
                }.bind(that)
            );
        },
        _initMove() {
            this.$nextTick(() => {
                const { switchDelay } = this.options;
                const { autoPlay, isHorizontal } = this;
                this._dataWarm(this.data);
                this.copyHtml = ""; //清空copy
                if (isHorizontal) {
                    this.height = this.$refs.wrap.offsetHeight;
                    this.width = this.$refs.wrap.offsetWidth;
                    let slotListWidth = this.$refs.slotList.offsetWidth;
                    // 水平滚动设置warp width
                    if (autoPlay) {
                        // 修正offsetWidth四舍五入
                        slotListWidth = slotListWidth * 2 + 1;
                    }
                    this.$refs.realBox.style.width = slotListWidth + "px";
                    this.realBoxWidth = slotListWidth;
                }

                if (autoPlay) {
                    this.ease = "ease-in";
                    this.delay = 0;
                } else {
                    this.ease = "linear";
                    this.delay = switchDelay;
                    return;
                }
                // 是否可以滚动判断
                if (this.scrollSwitch) {
                    let timer;
                    if (timer) clearTimeout(timer);
                    this.copyHtml = this.$refs.slotList.innerHTML;
                    setTimeout(() => {
                        this.realBoxHeight = this.$refs.realBox.offsetHeight;
                        this._move();
                        this.yPos =
                            -this.realSingleStopHeight * (this.data.length - 1);
                        this.$emit("singleStepEnd", {
                            topIndex: 0,
                            ...this.data[0]
                        });
                    }, 0);
                } else {
                    this._cancle();
                    this.yPos = this.xPos = 0;
                    //  停止滚动（不满足滚动条件）
                    // 空数据时
                    if (!this.data || this.data.length == 0) {
                        this.$emit("singleStepEnd", {
                            topIndex: -10
                        });
                    } else {
                        this.$emit("singleStepEnd", {
                            topIndex: 0,
                            ...this.data[0]
                        });
                    }
                }
            });
        },
        _dataWarm(data) {
            if (data.length > 100) {
                console.warn(
                    `数据达到了${data.length}条有点多哦~,可能会造成部分老旧浏览器卡顿。`
                );
            }
        },
        _startMove() {
            this.isHover = false; //开启_move
            this._move();
        },
        _stopMove() {
            this.isHover = true; //关闭_move
            // 防止频频hover进出单步滚动,导致定时器乱掉
            if (this.singleWaitTime) clearTimeout(this.singleWaitTime);
            this._cancle();
        }
    },
    mounted() {
        this._initMove();
        // this.$emit("singleStepEnd", this.data, this.rowIndex);
    },
    watch: {
        data(newData, oldData) {
            this._dataWarm(newData);
            //监听data是否有变更
            if (!arrayEqual(newData, oldData)) {
                this.reset();
            }
        },
        autoPlay(bol) {
            if (bol) {
                this.reset();
            } else {
                this._stopMove();
            }
        }
    },
    beforeCreate() {
        this.reqFrame = null; // move动画的animationFrame定时器
        this.singleWaitTime = null; // single 单步滚动的定时器
        this.isHover = false; // mouseenter mouseleave 控制this._move()的开关
        this.ease = "ease-in";
    },
    beforeDestroy() {
        this._cancle();
        clearTimeout(this.singleWaitTime);
    }
};
</script>
